#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

// 管程的初始化，这里的管程包含一个锁，还有两个条件变量
// // 这个 lock 用于保证互斥，只能有一个线程进入管程
pthread_mutex_t lock;

// 两个条件变量，一个用于控制窗口是否满了，一个用于控制窗口是否空
pthread_cond_t full;
pthread_cond_t empty;

// 表示总共的窗口数
int n = 2;
// 用于记录应经放置了菜的窗口数
int used = 0;

void produce(int chef_id) {
	// 进入管程，上锁，保证互斥，也就是保证只有 1 个线程在管程中执行
	pthread_mutex_lock(&lock);
	
	// 如果所有的窗口都放了菜，那么当前线程在条件变量 full 上等待
	while (used == n) {
		pthread_cond_wait(&full, &lock);
	}
		
	// 临界区
	printf("chef %d add food to food window\n", chef_id);
	fflush(stdout);
	used++;

	// 厨师将菜放入窗口，则通知 empty 等待队列中的服务员来取菜
	pthread_cond_signal(&empty);
	// 释放管程的锁
	pthread_mutex_unlock(&lock);
}

void consume(int waiter_id) {
	// 进入管程，上锁，保证互斥，也就是保证只有 1 个线程在管程中执行
	pthread_mutex_lock(&lock);
	// 如果所有的窗口都没有菜，那么当前线程在条件变量 empty 上等待
	while (used == 0) {
		pthread_cond_wait(&empty, &lock);
	}
	
	// 临界区
	printf("waiter %d remove food from food window\n", waiter_id);
	fflush(stdout);
	used--;

	// 当服务员拿完菜，则通知 full 等待队列中的厨师往窗口中放菜
	pthread_cond_signal(&full);
	// 释放管程的锁
	pthread_mutex_unlock(&lock);
}

void *chef_thread(void *args) {
	int chef_id = *((int *)args);
	while (1) {
		// 模拟做菜
		sleep(rand() % 5);
		produce(chef_id);
	}
}

void *waiter_thread(void *args) {
	int waiter_id = *((int *)args);
	while (1) {
		sleep(rand() % 3);
		consume(waiter_id);
	}
}

int main() {
	pthread_mutex_init(&lock, NULL);
	pthread_cond_init(&full, NULL);
	pthread_cond_init(&empty, NULL);
	
	pthread_t chefs[3];
	pthread_t waiters[2];
	int i;
	// 3 个厨师	
	for (i = 0; i < 3; i++) {
		pthread_create(&chefs[i], NULL, chef_thread, &i);
	}
	// 2 个服务员
	for (i = 0; i < 2; i++) {
		pthread_create(&waiters[i], NULL, waiter_thread, &i);
	}
		
	sleep(30);

	pthread_mutex_destroy(&lock);
	pthread_cond_destroy(&full);	
	pthread_cond_destroy(&empty);	
	return 1;
}
